/**************************************************************************************
*
*	Copyright (C) 2009-2010 Brent Andrew O. Chua - See LICENSE.txt for more info.
*		
*	Filename	-	FileIO.h
*
*	Description -	Wrapper for fStream operations.
*
*	Comments	-	
*
*	Modification History:
*			Name			   Date					Description
*			CodeSushi	-	01/10/2010		-	FileIO creation
*			CodeSushi	-	01/05/2011		-	ANSI C Specification
**************************************************************************************/

#ifndef PSX_FILEIO_H_
#define PSX_FILEIO_H_

#include <stdio.h>
#include <string>

using std::string;

class FileIO
{
public:

	typedef int FILEOP;

	enum {
		FILEOP_READ			=	0x01, 
		FILEOP_WRITE		=	0x02,
		FILEOP_BINARY		=	0x04,
		FILEOP_TRUNCATE		=	0x08,
		FILEOP_APPENED		=	0x10,
		FILEOP_GOTO_END		=	0x20,
		FILEOP_FORCE_DWORD	=	0x7fffffff
	};

	//enum SEEK_OPTION {
	//	SEEKOP_BEGIN	=	SEEK_SET,
	//	SEEKOP_END		=	SEEK_END,
	//	SEEKOP_CURRENT	=	SEEK_CUR,
	//};

	enum SEEK_OPTION {
		SEEKOP_BEGIN	=	SEEK_SET,
		SEEKOP_END		=	SEEK_END,
		SEEKOP_CURRENT	=	SEEK_CUR,
	};

	FileIO( void );
	FileIO( const char *pFileName, FILEOP options );
	~FileIO( void );

	bool Open( const char *pFileName, FILEOP options );
	void Close( void );

	// NOTE: Read and Write methods are templated so that we don't have to typecast the pointer to (char *).
	// WARNING: Doing this method also accepts double, tripple, etc... pointers. This will result in an 
	//	undefined behavior. YOU SHOULD ONLY PASS SINGLE POINTERS.
	template < typename T >
	unsigned int Write( const T *pStr, unsigned int size );
	virtual unsigned int Write( const char *pStr, unsigned int size );

	template < typename T >
	unsigned int Read( T *pOutput, unsigned int size );
	virtual unsigned int Read( char *pOutput, unsigned int size );

	template < typename T >
	unsigned int WriteString( const T *pStr, unsigned int size );
	unsigned int WriteString( const char *pStr, unsigned int size );
	unsigned int WriteString( const char *pStr );

	template < typename T >
	unsigned int ReadString( T *pOutput, unsigned int size );
	unsigned int ReadString( char *pOutput, unsigned int size );

	virtual unsigned int Write( const char *pVal );
	virtual unsigned int Write( const unsigned char *pVal );
#if defined( PSX_UNICODE )
	virtual unsigned int Write( const char *pVal );
#endif /* PSX_UNICODE  */
	virtual unsigned int Write( const int *pVal );
	virtual unsigned int Write( const unsigned int *pVal );
	virtual unsigned int Write( const short *pVal );
	virtual unsigned int Write( const unsigned short *pVal );
	virtual unsigned int Write( const long *pVal );
	virtual unsigned int Write( const unsigned long *pVal );
	virtual unsigned int Write( const long long *pVal );
	virtual unsigned int Write( const unsigned long long *pVal );
	virtual unsigned int Write( const float *pVal );
	virtual unsigned int Write( const double *pVal );

	virtual unsigned int Read( char *pVal );
	virtual unsigned int Read( unsigned char *pVal );
#if defined( PSX_UNICODE )
	virtual unsigned int Read( char *pVal );
#endif /* PSX_UNICODE  */
	virtual unsigned int Read( int *pVal );
	virtual unsigned int Read( unsigned int *pVal );
	virtual unsigned int Read( short *pVal );
	virtual unsigned int Read( unsigned short *pVal );
	virtual unsigned int Read( long *pVal );
	virtual unsigned int Read( unsigned long *pVal );
	virtual unsigned int Read( long long *pVal );
	virtual unsigned int Read( unsigned long long *pVal );
	virtual unsigned int Read( float *pVal );
	virtual unsigned int Read( double *pVal );

	virtual FileIO & operator << ( const char &val );
	virtual FileIO & operator << ( const unsigned char &val );
#if defined( PSX_UNICODE )
	virtual FileIO & operator << ( const char &val );
#endif /* PSX_UNICODE */
	virtual FileIO & operator << ( const int &val );
	virtual FileIO & operator << ( const unsigned int &val );
	virtual FileIO & operator << ( const short &val );
	virtual FileIO & operator << ( const unsigned short &val );
	virtual FileIO & operator << ( const long &val );
	virtual FileIO & operator << ( const unsigned long &val );
	virtual FileIO & operator << ( const long long &val );
	virtual FileIO & operator << ( const unsigned long long &val );
	virtual FileIO & operator << ( const float &val );
	virtual FileIO & operator << ( const double &val );

	virtual FileIO & operator >> ( char &val );
	virtual FileIO & operator >> ( unsigned char &val );
#if defined( PSX_UNICODE )
	virtual FileIO & operator >> ( char &val );
#endif /* PSX_UNICODE */
	virtual FileIO & operator >> ( int &val );
	virtual FileIO & operator >> ( unsigned int &val );
	virtual FileIO & operator >> ( short &val );
	virtual FileIO & operator >> ( unsigned short &val );
	virtual FileIO & operator >> ( long &val );
	virtual FileIO & operator >> ( unsigned long &val );
	virtual FileIO & operator >> ( long long &val );
	virtual FileIO & operator >> ( unsigned long long &val );
	virtual FileIO & operator >> ( float &val );
	virtual FileIO & operator >> ( double &val );

	const char * GetLine( string &str );

	// NOTE: This works for both read and write.
	FileIO & Seek( int pos, SEEK_OPTION option );
	FileIO & Seek( int pos );
	FileIO & Seek64( long long pos, SEEK_OPTION option );
	FileIO & Seek64( long long pos );

	int Tell( void );
	long long Tell64( void );

	virtual FileIO & Flush( void ); // The toilet?

	bool IsOpen( void );
	bool IsEOF( void );
	bool IsOK( void ); // Check for the bad failbit
	bool IsError( void );
	virtual bool IsWritable( void );
	virtual bool IsReadable( void );

	void ClearError( void );

private:

	FILEOP  m_fileOP;
	FILE	*m_pFStream;

};

inline FileIO::FileIO( void )
: m_pFStream( NULL )
{

}

inline FileIO::FileIO( const char *pFileName, FILEOP options )
: m_pFStream( NULL )
{
	Open( pFileName, options );
}

inline FileIO::~FileIO( void )
{
	Close();
}

inline void FileIO::Close( void )
{
	if ( m_pFStream )
	{
		// Automaticaly flushes the stream buffer
		fclose( m_pFStream );
		m_pFStream = NULL;
		m_fileOP = 0;
	}
}

template < typename T >
inline unsigned int FileIO::Write( const T *pStr, unsigned int size )
{
	return Write( (const char *)pStr, size );
}

template < typename T >
inline unsigned int FileIO::Read( T *pOutput, unsigned int size )
{
	return Read( (char*)pOutput, size );
}

inline unsigned int FileIO::Write( const char *pStr, unsigned int size )
{
	return fwrite( (void *)pStr, sizeof(char), size, m_pFStream );
}

inline unsigned int FileIO::Read( char *pOutput, unsigned int size )
{
	return fread( (void*)pOutput, sizeof(char), size, m_pFStream );
}

inline unsigned int FileIO::Write( const char *pVal )
{
	return Write( &pVal, sizeof(char) );
}

inline unsigned int FileIO::Write( const unsigned char *pVal )
{
	return Write( pVal, sizeof(unsigned char) );
}

#if defined( PSX_UNICODE )
inline unsigned int FileIO::Write( const char *pVal )
{
	return Write( pVal, sizeof(char) );
}
#endif /* PSX_UNICODE  */

inline unsigned int FileIO::Write( const int *pVal )
{
	return Write( pVal, sizeof(int) );
}

inline unsigned int FileIO::Write( const unsigned int *pVal )
{
	return Write( pVal, sizeof(unsigned int) );
}

inline unsigned int FileIO::Write( const short *pVal )
{
	return Write( pVal, sizeof(short) );
}

inline unsigned int FileIO::Write( const unsigned short *pVal )
{
	return Write( pVal, sizeof(unsigned short) );
}

inline unsigned int FileIO::Write( const long *pVal )
{
	return Write( pVal, sizeof(long) );
}

inline unsigned int FileIO::Write( const unsigned long *pVal )
{
	return Write( pVal, sizeof(unsigned long) );
}

inline unsigned int FileIO::Write( const long long *pVal )
{
	return Write( pVal, sizeof(long long) );
}

inline unsigned int FileIO::Write( const unsigned long long *pVal )
{
	return Write( pVal, sizeof(unsigned long long) );
}

inline unsigned int FileIO::Write( const float *pVal )
{
	return Write( pVal, sizeof(float) );
}

inline unsigned int FileIO::Write( const double *pVal )
{
	return Write( pVal, sizeof(double) );
}

inline unsigned int FileIO::Read( char *pVal )
{
	return Read( pVal, sizeof(char) );
}

inline unsigned int FileIO::Read( unsigned char *pVal )
{
	return Read( pVal, sizeof(unsigned char) );
}

#if defined( PSX_UNICODE )
inline unsigned int FileIO::Read( char *pVal )
{
	return Read( pVal, sizeof(char) );
}
#endif /* PSX_UNICODE  */

inline unsigned int FileIO::Read( int *pVal )
{
	return Read( pVal, sizeof(int) );
}

inline unsigned int FileIO::Read( unsigned int *pVal )
{
	return Read( pVal, sizeof(unsigned int) );
}

inline unsigned int FileIO::Read( short *pVal )
{
	return Read( pVal, sizeof(short) );
}

inline unsigned int FileIO::Read( unsigned short *pVal )
{
	return Read( pVal, sizeof(unsigned short) );
}

inline unsigned int FileIO::Read( long *pVal )
{
	return Read( pVal, sizeof(long) );
}

inline unsigned int FileIO::Read( unsigned long *pVal )
{
	return Read( pVal, sizeof(unsigned long) );
}

inline unsigned int FileIO::Read( long long *pVal )
{
	return Read( pVal, sizeof(long long) );
}

inline unsigned int FileIO::Read( unsigned long long *pVal )
{
	return Read( pVal, sizeof(unsigned long long) );
}

inline unsigned int FileIO::Read( float *pVal )
{
	return Read( pVal, sizeof(float) );
}

inline unsigned int FileIO::Read( double *pVal )
{
	return Read( pVal, sizeof(double) );
}

template < typename T >
inline unsigned int FileIO::WriteString( const T *pStr, unsigned int size )
{
	//return fwrite( (void *)pStr, sizeof(char), size, m_pFStream );
	return WriteString( (const char *)pStr, size );
}

inline unsigned int FileIO::WriteString( const char *pStr, unsigned int size )
{
	return fwrite( (void *)pStr, sizeof(char), size, m_pFStream );
}

inline unsigned int FileIO::WriteString( const char *pStr )
{
	return fwrite( (void *)pStr, sizeof(char), strlen( pStr ), m_pFStream );
}

template < typename T >
inline unsigned int FileIO::ReadString( T *pOutput, unsigned int size )
{
	//return fread( (void*)pOutput, sizeof(char), size, m_pFStream );
	return ReadString( (char*)pOutput, size );
}

inline unsigned int FileIO::ReadString( char *pOutput, unsigned int size )
{
	return fread( (void*)pOutput, sizeof(char), size, m_pFStream );
}

inline FileIO & FileIO::operator << ( const char &val )
{
	Write( &val );
	return *this;
}

inline FileIO & FileIO::operator << ( const unsigned char &val )
{
	Write( &val );
	return *this;
}

#if defined( PSX_UNICODE )
inline FileIO & FileIO::operator << ( const char &val )
{
	Write( &val );
	return *this;
}
#endif /* PSX_UNICODE */

inline FileIO & FileIO::operator << ( const int &val )
{
	Write( &val );
	return *this;
}

inline FileIO & FileIO::operator << ( const unsigned int &val )
{
	Write( &val );
	return *this;
}

inline FileIO & FileIO::operator << ( const short &val )
{
	Write( &val );
	return *this;
}

inline FileIO & FileIO::operator << ( const unsigned short &val )
{
	Write( &val );
	return *this;
}

inline FileIO & FileIO::operator << ( const long &val )
{
	Write( &val );
	return *this;
}

inline FileIO & FileIO::operator << ( const unsigned long &val )
{
	Write( &val );
	return *this;
}

inline FileIO & FileIO::operator << ( const long long &val )
{
	Write( &val );
	return *this;
}

inline FileIO & FileIO::operator << ( const unsigned long long &val )
{
	Write( &val );
	return *this;
}

inline FileIO & FileIO::operator << ( const float &val )
{
	Write( &val );
	return *this;
}

inline FileIO & FileIO::operator << ( const double &val )
{
	Write( &val );
	return *this;
}

inline FileIO & FileIO::operator >> ( char &val )
{
	Read( &val );
	return *this;
}

inline FileIO & FileIO::operator >> ( unsigned char &val )
{
	Read( &val );
	return *this;
}

#if defined( PSX_UNICODE )
inline FileIO & FileIO::operator >> ( char &val )
{
	Read( &val );
	return *this;
}
#endif /* PSX_UNICODE */

inline FileIO & FileIO::operator >> ( int &val )
{
	Read( &val );
	return *this;
}

inline FileIO & FileIO::operator >> ( unsigned int &val )
{
	Read( &val );
	return *this;
}

inline FileIO & FileIO::operator >> ( short &val )
{
	Read( &val );
	return *this;
}

inline FileIO & FileIO::operator >> ( unsigned short &val )
{
	Read( &val );
	return *this;
}

inline FileIO & FileIO::operator >> ( long &val )
{
	Read( &val );
	return *this;
}

inline FileIO & FileIO::operator >> ( unsigned long &val )
{
	Read( &val );
	return *this;
}

inline FileIO & FileIO::operator >> ( long long &val )
{
	Read( &val );
	return *this;
}

inline FileIO & FileIO::operator >> ( unsigned long long &val )
{
	Read( &val );
	return *this;
}

inline FileIO & FileIO::operator >> ( float &val )
{
	Read( &val );
	return *this;
}

inline FileIO & FileIO::operator >> ( double &val )
{
	Read( &val );
	return *this;
}

// NOTE: This works for both read and write.
inline FileIO & FileIO::Seek( int pos, SEEK_OPTION option )
{
	fseek( m_pFStream, pos, option );
	return *this;
}

inline FileIO & FileIO::Seek( int pos )
{
	fseek( m_pFStream, pos, SEEKOP_BEGIN );
	return *this;
}

inline FileIO & FileIO::Seek64( long long pos, SEEK_OPTION option )
{
	_fseeki64( m_pFStream, pos, option );
	return *this;
}

inline FileIO & FileIO::Seek64( long long pos )
{
	_fseeki64( m_pFStream, pos, SEEKOP_BEGIN );
	return *this;
}

inline int FileIO::Tell( void )
{
	return ftell( m_pFStream );
}

inline long long FileIO::Tell64( void )
{
	return _ftelli64( m_pFStream );
}

inline FileIO & FileIO::Flush( void ) // The toilet?
{
	fflush( m_pFStream );
	return *this;
}

inline bool FileIO::IsEOF( void )
{	
	return feof( m_pFStream ) != 0;
}

inline bool FileIO::IsOpen( void )
{
	return m_pFStream != NULL;
}

inline bool FileIO::IsOK( void )
{
	return !IsError();
}

inline bool FileIO::IsError( void )
{
	return (ferror( m_pFStream ) == 0);
}

inline bool FileIO::IsWritable( void )
{
	return (m_fileOP & FILEOP_WRITE) != 0;
}

inline bool FileIO::IsReadable( void )
{
	return (m_fileOP & FILEOP_READ) != 0;
}

inline void FileIO::ClearError( void )
{
	clearerr( m_pFStream );
}

#endif /* PSX_FILEIO_H_ */